Skip to content

Conversation

@4l0n50
Copy link
Contributor

@4l0n50 4l0n50 commented Nov 6, 2025

Currently, witness hints can be declared using alloc_witness_hints, but there is no defined mechanism for how these values are populated during circuit evaluation. This makes it unclear how unconstrained data should be assigned within the witness table.

This PR introduces a new primitive operation, Op::Unconstrained, which assigns the values of witness hints. During evaluation time, these values are populated according to the behavior defined by the new WitnessHintFiller trait. Implementors of this trait define the logic for deriving hint values, potentially using existing wire values already computed in the circuit.

@4l0n50 4l0n50 self-assigned this Nov 6, 2025
Copy link
Contributor

@Nashtare Nashtare left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Alonso!

As a general comment, I feel like Unconstrained is a bit misleading, as we may still apply constraints on these values (like in hash_squeeze for instance), so perhaps it'd be worth thinking of another name, maybe something like Op::Private or anything that reflects the underlying logic?

Comment on lines +493 to +496
println!("=== CIRCUIT PRIMITIVE OPERATIONS ===");
for (i, prim) in circuit.primitive_ops.iter().enumerate() {
println!("{i}: {prim:?}");
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove all those prints?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also below

Copy link
Contributor Author

@4l0n50 4l0n50 Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are the same as in the original test. I forgot to mention that I think the new test "Proves that we know x such that 37 * x - 111 = 0" (like a NP statement), as the comment says. In the original the solution is also know to the verifer, so it's more like "Proves that 3 is a solution to 37 * x - 111 = 0" (P statement). Should we keep both tests?

4l0n50 and others added 4 commits November 7, 2025 16:31
Co-authored-by: Robin Salen <30937548+Nashtare@users.noreply.github.com>
@4l0n50
Copy link
Contributor Author

4l0n50 commented Nov 7, 2025

As a general comment, I feel like Unconstrained is a bit misleading, as we may still apply constraints on these values (like in hash_squeeze for instance), so perhaps it'd be worth thinking of another name, maybe something like Op::Private or anything that reflects the underlying logic?

In my opinion it's is indeed unconstrained, until you push the non-primitive operation (e.g. what should happen in add_hash_squeeze) or add more gates for checking the hints (e.g. the binary decomposition). But you could also not add any constraint.

&self,
traces: &Traces<EF>,
pis: &Vec<Val<SC>>,
pis: &[Val<SC>],
Copy link
Contributor Author

@4l0n50 4l0n50 Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This (and other changes) have nothing to do with the PR, but clippy was complaining

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes it's been merged on the Plonky3 side

bits.push(bit);
}
let binary_decomposition_hint = BinaryDecompositionHint::new(x, n_bits)?;
let bits = builder.alloc_witness_hints(binary_decomposition_hint, "decompose_to_bits");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add back builder.assert_bool(bit);?

Copy link
Contributor

@LindaGuiga LindaGuiga left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

use alloc::{format, vec};
use core::fmt::Debug;
use core::iter;
use core::result::Result;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this (still) used in the file?

assert_eq!(traces.add_trace.lhs_values.len(), 1);
assert_eq!(traces.add_trace.lhs_index, vec![WitnessId(2)]);
assert_eq!(traces.add_trace.rhs_index, vec![WitnessId(0)]);
assert_eq!(traces.add_trace.result_index, vec![WitnessId(4)]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add an assert that the x value is 3?

.map(|&index_target| {
let all_bits = decompose_to_bits(circuit, index_target, MAX_QUERY_INDEX_BITS);
let all_bits =
decompose_to_bits(circuit, index_target, MAX_QUERY_INDEX_BITS).unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we map the CircuitError to a VerificationError instead of using unwrap() here?


// Decompose to bits (adds public inputs for each bit and verifies they reconstruct x)
let bits = decompose_to_bits(circuit, x, total_num_bits);
let bits = decompose_to_bits(circuit, x, total_num_bits).unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should make the method return an Error instead of unwrapping

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

5 participants